---
sidebar_position: 1
---

# Asp.NET Core的依赖注入

## 1. 什么是依赖注入
依赖注入（DI）是一种设计模式，它允许类依赖于外部类的实例而不需要自己创建这些实例。在依赖注入中，类的依赖关系由外部容器来管理和注入。

## 2. 什么是控制反转
控制反转（IoC）是依赖注入的一种实现方式。它是一种软件设计原则，其中控制权由外部容器控制。在控制反转中，类不再负责自己的依赖关系的创建和管理，而是由外部容器来负责。

## 3. IoC/DI的优点和缺点
### 优点
- 降低耦合性：依赖注入可以降低类之间的耦合性，使得类更容易被重用和测试。
- 提高可测试性：依赖注入可以使得类的依赖关系更容易被替换和模拟，从而提高类的可测试性。
- 提高灵活性：依赖注入可以使得类的依赖关系更容易被替换和修改，从而提高类的灵活性。

### 缺点
- 学习成本：依赖注入需要对依赖注入容器和相关的设计模式有一定的了解和学习成本。
- 增加复杂性：依赖注入可能会增加代码的复杂性，特别是在大型项目中。

## 4. 依赖注入的方式
### 构造函数注入
构造函数注入是最常见的依赖注入方式，它通过类的构造函数来注入依赖关系。
```csharp
public class MyService
{
    private readonly IMyDependency _myDependency;

    public MyService(IMyDependency myDependency)
    {
        _myDependency = myDependency;
    }
}
```

### 方法注入
方法注入是通过类的方法来注入依赖关系。
```csharp
public class MyService
{
    private IMyDependency _myDependency;

    public void SetDependency(IMyDependency myDependency)
    {
        _myDependency = myDependency;
    }
}
```

### 属性注入（默认框架并且没有实现，Autofac框架实现了属性注入）
属性注入是通过类的属性来注入依赖关系。
```csharp
public class MyService
{
    public IMyDependency MyDependency { get; set; }
}
```


:::warning


属性注入是一种依赖注入的方式，它允许将依赖关系通过属性来注入到类中。属性注入有一些优点和缺点，让我们来详细讨论一下。

 - 优点：
    - 灵活性：属性注入允许在不修改类构造函数的情况下，向类中注入新的依赖关系。这使得类的依赖关系更加灵活，可以更容易地进行替换和修改。
    - 可读性：属性注入可以使代码更加清晰易懂，因为依赖关系是通过属性直接暴露出来的，而不是隐藏在构造函数中。
    - 可选性：属性注入可以将依赖关系标记为可选的，这意味着如果没有提供相应的依赖关系，类仍然可以正常工作。

- 缺点：
    - 隐藏依赖关系：属性注入将依赖关系隐藏在类的属性中，这可能会导致类的依赖关系不够明显，增加了理解和维护的难度。
    - 难以测试：属性注入使得依赖关系难以在单元测试中进行模拟和替换，因为依赖关系是直接暴露在属性中的，而不是通过构造函数传入的。
    - 运行时错误：由于属性注入是在运行时进行的，因此如果依赖关系未正确注入，可能会导致运行时错误，而这些错误在编译时是无法捕获的。

:::

## 5. 在MiniApis中使用依赖注入的相关案例和讲解
在MiniApis中，可以使用依赖注入来管理服务的依赖关系。以下是一个简单的示例：

```csharp
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddTransient<IMyDependency, MyDependency>();
builder.Services.AddTransient<MyService>();

var app = builder.Build();

app.MapGet("/", (IMyDependency myDependency) =>
{
    myDependency.DoSomething();
});

app.MapPost("/", (MyService myDependency) =>
{
    myDependency.DoSomething();
});

app.MapPut("/", ([FromServices]IMyDependency myDependency) =>
{
    myDependency.DoSomething();
});


public interface IMyDependency
{
    void DoSomething();
}

public class MyDependency : IMyDependency
{
    public void DoSomething()
    {
        Console.WriteLine("MyDependency is doing something");
    }
}

public class MyService
{
    private readonly IMyDependency _myDependency;

    public MyService(IMyDependency myDependency)
    {
        _myDependency = myDependency;
    }

    public void DoSomething()
    {
        _myDependency.DoSomething();
    }
}

```

在上面的示例中，我们使用`builder.Services`注入了俩个相应的服务，然后在`app.MapPost`和`app.MapGet`,`app.MapPut`中使用依赖注入来注入相应的服务。注入方式有三种，分别是构造函数注入、方法注入和属性注入。在`app.MapGet`中，我们使用了构造函数注入，`app.MapPost`中使用了方法注入，`app.MapPut`中使用了属性注入。

## 6. 注册对象生命周期
在依赖注入中，对象的生命周期可以分为三种：
- 瞬时生命周期（Transient）：每次请求都会创建一个新的实例。
    - 在WebApi中每次获取服务都会创建一个新的实例，因此在同一个请求中，会创建多个实例。
    - 在控制台应用程序中，每次调用`IServiceProvider.GetService`方法都会创建一个新的实例。
- 作用域生命周期（Scoped）：每次请求都会创建一个新的实例，但在同一个作用域中，会共享同一个实例。
    - 在WebApi中，每个请求都会创建一个新的作用域，因此在同一个请求中，会共享同一个实例。
    - 在控制台应用程序中，每次调用`IServiceProvider.CreateScope`方法都会创建一个新的作用域，因此在同一个作用域中，会共享同一个实例。

- 单例生命周期（Singleton）：整个应用程序生命周期中只会创建一个实例。
    - 在WebApi中，每个应用程序都会创建一个单例实例。（一般会将某些静态的方法改成单例的方式进行管理）
```csharp
services.AddTransient<IMyDependency, MyDependency>(); // 瞬时生命周期
services.AddScoped<IMyDependency, MyDependency>(); // 作用域生命周期
services.AddSingleton<IMyDependency, MyDependency>(); // 单例生命周期
```

## 7. 实现自动注入响应的生命周期
可以通过接口的默认实现来实现自动注入响应的生命周期。例如，我们可以定义一个`IMyDependency`接口和其默认实现`MyDependency`，然后在注册服务时使用接口和默认实现的方式：

```csharp
public interface IMyDependency
{
    void DoSomething();
}

public class MyDependency : IMyDependency
{
    public void DoSomething()
    {
        Console.WriteLine("MyDependency is doing something");
    }
}

services.AddTransient<IMyDependency, MyDependency>();
```

在上面的示例中，我们注册了`IMyDependency`接口和`MyDependency`默认实现，并指定了瞬时生命周期。当我们在其他类中注入`IMyDependency`时，依赖注入容器会自动注入`MyDependency`的实例，并且会根据注册时指定的生命周期来管理实例的生命周期。